package cpr.context;


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Pattern;

import cpr.DefaultCommandHandler;
import cpr.MainConst;
import cpr.Printer;
import cpr.Rule;
import cpr.Utils;
import cpr.context.ruleacceptor.GlobalRuleNrSetRuleAcceptor;
import cpr.context.ruleacceptor.IRuleAcceptor;
import cpr.context.ruleacceptor.SolveTrueRuleAcceptor;
import cpr.context.ruleacceptor.WorksetItemRuleDigitalAcceptor;
import cpr.data.AR;
import cpr.data.AREntry;
import cpr.data.AREntryComparator;
import cpr.data.AttrFreq;
import cpr.data.AttrFreqComparator;
import cpr.data.HLVector;
import cpr.data.SolveBestRules;
import cpr.data.SolveBestRulesComparator;
import cpr.data.SolveBestRulesEntry;
import cpr.data.SolveCandidates2;
import cpr.helper.HashCodeUtil;
import cpr.helper.ScoreHelper;
import data.IWorksetItem;

public class SolveContext implements IPurposeContext {
	public SolveContext(WorksetContext wctx, int wsId) {
		this.wctx = wctx;
		this.ctx = wctx.getContext();
		worksetId = wsId;
	}
	private int worksetId = -1;
	public List hl_rules = null;
    public int min_unccovered = -1;
    private Map atrFreqMap = null;
    private Set attrValuesDigital = null;
    private int[] attrFreqDigital = null;
    private Map attrFreqDigitalMap = null;
    private TreeSet sortedAfSet = null;
    public int rules_number =0;
    public long[] candidatesArray;
    public int[] eval_rules;
    public long[] eval_rules_plus;
    public long[] eval_rules_minus;
    public int[][] hl_rules_hashcodes=null;
    public int hl_rules_hashcode_size = 0;
    
    public SolveCandidates2 candidates2;
    public List hl_vectors = null;
    public SolveBestRules bestRules;
   // public SortedSet ss = new TreeSet(new AREntryComparator());
    private AREntryComparator ARENTRY_COMPARATOR = new AREntryComparator();
    public long TIMER1;
    public long TIMER2;
    public long TIMER3;
//    public int min_cov = -1;
    public int getWorksetId() {
    	return worksetId;
    }

//    public void execCommand() {
//    	command.applyExternalProps();
//    	if (command.getProperty("echo")!=null) {
//    		Printer.println("external command");
//    		SolveBestRulesEntry best = bestRules.getBest();
//    		AREntry[] ares = best.ares;
//			Printer.println(""+best.uncovered+" uncovered:");
//    		for (int i = 0; i < ares.length; i++) {
//				String s = ares[i].rule.ruleNr+" "+ares[i].vector.score;
//				Printer.println(s);
//			}
//			Printer.println("---");
//    		command.setProperty("echo",null);
//    	}
//    }
    
    private WorksetContext wctx;
    private Context ctx;
    public Context getContext() {
    	return ctx;
    }
    public WorksetContext getWorksetContext() {
    	return wctx;
    }

    public void hl_analyse() {
    	hl_analyse(ctx.getRootScore(),new SolveTrueRuleAcceptor());
    }
    
    SolveBestRulesEntry solution = null;
	
    public SolveBestRulesEntry getSolution() {
    	return solution;
    }

    private void tst_copy_eval_bin(long[] target_plus, long[] target_minus) {
    	for (int i = 0; i < target_plus.length; i++) {
			target_plus[i] = eval_rules_plus[i];
			target_minus[i] = eval_rules_minus[i];
		}
    }
    
    private void tst_ASSERT_compare_eval_bins(
    		long[] target_plus1, long[] target_minus1, 
    		long[] target_plus2, long[] target_minus2) {
    	for (int i = 0; i < target_plus1.length; i++) {
			if (target_plus1[i]!=target_plus2[i]) {
				throw new RuntimeException("pluses are different in plus "+i);
			}
		}
    	for (int i = 0; i < target_minus1.length; i++) {
			if (target_minus1[i]!=target_minus2[i]) {
				throw new RuntimeException("pluses are different in minus "+i);
			}
		}
    }
    
    
    public void tst_perf_GET_AR(int size, int base_score, int count) {
    	Printer.println(Printer.currDate()+" tst_perf_GET_AR...");
    	IWorksetItem wi =getWorksetContext().getWorksetItemAt(worksetId);
		WorksetItemRuleDigitalAcceptor acc = new WorksetItemRuleDigitalAcceptor(getContext(),wi);
		
    	init_load_rules(base_score,acc);
    	init_const();
    	compute_hash_codes_for_rules(hl_vectors.size());
    	int c = 0;
    	while (c<count) {
        	int i = 0;
        	AREntry[] ares = new AREntry[size];
        	while(i<size) {
        		AR nextAr = candidates2.getAR(i++, getContext().getRootScore());
        		Printer.println();
    		}
    		c++;
    	}
    		
    }
    
    public AREntry[] tst_createTestEval(int size, int base_score, int target_score, int eval_count) {
    	Printer.println(Printer.currDate()+" tst_createTestAres...");
    	IWorksetItem wi =getWorksetContext().getWorksetItemAt(worksetId);
		WorksetItemRuleDigitalAcceptor acc = new WorksetItemRuleDigitalAcceptor(getContext(),wi);
		
    	init_load_rules(base_score,acc);
    	init_const();
    	
    	
    	int ptr = 0;
    	int i = 0;
    	AREntry[] ares = new AREntry[size];
    	while(ptr<size) {
    		AR nextAr = candidates2.getAR(i++, getContext().getRootScore());
    		for (Iterator iter = nextAr.entriesList.iterator(); iter.hasNext();) {
                AREntry are = (AREntry)iter.next();
                if (target_score==are.vector.score) {
            		ares[ptr++] = are;
                	break;
                }
            }
    		Printer.println();
		}
    	long[] eval_rules_plus1 = new long[eval_rules_plus.length];
        long[] eval_rules_minus1= new long[eval_rules_plus.length];
    	long[] eval_rules_plus2= new long[eval_rules_plus.length];
        long[] eval_rules_minus2= new long[eval_rules_plus.length];
    	long[] eval_rules_plus3= new long[eval_rules_plus.length];
        long[] eval_rules_minus3= new long[eval_rules_plus.length];
        
    	Printer.println(Printer.currDate()+" evaluating without groups "+eval_count+" times");
    	for (int j = 0; j < eval_count; j++) {
        	eval_rules_bin(ares);
		}
    	Printer.println(Printer.currDate()+" copying...");
    	tst_copy_eval_bin(eval_rules_plus1,eval_rules_minus1);
    	Printer.println(Printer.currDate()+" evaluating with groups "+eval_count+" times");
    	for (int j = 0; j < eval_count; j++) {
        	eval_rules_by_groups_bin(ares);
		}
    	Printer.println(Printer.currDate()+" copying...");
    	tst_copy_eval_bin(eval_rules_plus2,eval_rules_minus2);
    	Printer.println(Printer.currDate()+" evaluating with groups and unroll "+eval_count+" times");
    	for (int j = 0; j < eval_count; j++) {
        	eval_rules_by_groups_and_unroll_bin(ares);
		}
    	Printer.println(Printer.currDate()+" copying...");
    	tst_copy_eval_bin(eval_rules_plus3,eval_rules_minus3);
    	Printer.println(Printer.currDate()+" ok");
    	
    	tst_ASSERT_compare_eval_bins(
    			eval_rules_plus1, eval_rules_minus1,
    			eval_rules_plus2, eval_rules_minus2
    			);
    	tst_ASSERT_compare_eval_bins(
    			eval_rules_plus1, eval_rules_minus1,
    			eval_rules_plus3, eval_rules_minus3
    			);
    	return ares;
    }
    
    
    public SolveBestRulesEntry loadSolution(boolean tmp) {
    	try {
    		solution = null;
        	String filename = getWorksetSolutionFilePath(tmp);
    		File file = new File(filename);
    		if (!file.exists()) {
    			Printer.println("File "+filename+" does not exist.");
    			return null;
    		}
    		Printer.println("Loading "+filename);
    		Reader r = new FileReader(file);
    		BufferedReader br = new BufferedReader(r);
    		String s = null;
    		Pattern spacePattern = Pattern.compile( "\\s" );
    		Set ruleNrs = new HashSet();
    		Set ruleNrGlobals = new HashSet();
    		solution = new SolveBestRulesEntry(this);
    		solution.uncovered = 0;
    		List aREntries = new ArrayList();
    		do {
    			s= br.readLine();
    			if ((s!=null)&&(!s.startsWith("#"))) {
    				String[] st = spacePattern.split(s);
    				if (st.length!=3) {
    					Printer.println("format error: "+ s);
    				} else {
//    					Rule.totalRuleNr = 0;
    		            //Printer.println("st[0]"+st[0]+" st[1]="+st[1]);
    					Integer ruleNr = null;
    					Integer ruleNrGlobal = null;
    					HLVector vector = null;
    					try {
    						ruleNr = new Integer(st[0]);
    						ruleNrGlobal = new Integer(st[1]);
    						ruleNrs.add(ruleNr);
    						ruleNrGlobals.add(ruleNrGlobal);
    						vector = new HLVector(Integer.parseInt(st[2]));
    						
    						
    						AREntry a = new AREntry();
    						Rule dummyRule = new Rule();
    						dummyRule.ruleNr = ruleNr.intValue();
    						a.rule = dummyRule;
    						a.vector = vector;
    						aREntries.add(a);
    												
    					} catch (Exception ex) {
    						throw new RuntimeException(ex);
    					}
    					
    					
    				}
    			}
    		} while(s!=null);
    		
    		//WorksetLoaderRuleAcceptor ruleAcceptor = new WorksetLoaderRuleAcceptor(ruleNrs);
    		IWorksetItem wi = getWorksetContext().getWorksetItemAt(getWorksetId());
    		WorksetItemRuleDigitalAcceptor ruleAcceptor = new WorksetItemRuleDigitalAcceptor(getContext(),wi);
    		String csv_file  =  ctx.getPureDigitalDataFilePath();
    		
    		hl_rules = ctx.getIOContext().readRulesFromPureDigitalFile(csv_file, ctx.getRulesToLoad(),ruleAcceptor);
            
    		//List hl_rules = ctx.getIOContext().readRulesFromPureDigitalFile(csv_file, ctx.getRulesToLoad(),ruleAcceptor);
            
//    		hl_rules = Utils.readRulesFromHLFile(
//    				getCommandContext().getContext().getDigitalDataFilePath(), -1,false, ruleAcceptor);
            for (Iterator iter = hl_rules.iterator(); iter.hasNext();) {
    			Rule aRule = (Rule) iter.next();
    			for (Iterator iterator = aREntries.iterator(); iterator
    					.hasNext();) {
    				AREntry a = (AREntry) iterator.next();
    				if (a.rule.ruleNr == aRule.ruleNr) {
    					a.rule = aRule;
    				}
//    				if (!ruleNrGlobals.contains(Integer.valueOf(aRule.ruleNrGlobal))) {
//    					throw new RuntimeException("loaded rule has wrong ruleNrGlobal: "+aRule.ruleNrGlobal);
//    				}
    			}
    		}
    		br.close();
    		solution.ares = (AREntry[])aREntries.toArray(new AREntry[aREntries.size()]);
    		return solution;
    	} catch (Exception ex) {
    		throw new RuntimeException(ex);
    	}
		
    }
    public int getLastMatchingAREntry(SolveBestRulesEntry aSolution, Rule aRule) {
    	int lastMatchedAREntry = -1;
    	int lastScore = -1;
    	for (int i = 0; i < aSolution.ares.length; i++) {
    		AREntry arentry = aSolution.ares[i];
    		if (lastScore!=-1) {
    			if (lastScore>arentry.vector.score) {
    				throw new RuntimeException("lastScore more than current score of "+arentry);
    			}
    		}
    		lastScore = arentry.vector.score;
    		if (isSolutionItemCoversRule(arentry, aRule)) {
    			lastMatchedAREntry = i;
    		}
		}
    	return lastMatchedAREntry;
    }
    
    
    
    public List getAlltMatchingAREntries(SolveBestRulesEntry aSolution, Rule aRule) {
    	List entries = new ArrayList();
    	int lastScore = -1;
    	for (int i = 0; i < aSolution.ares.length; i++) {
    		AREntry arentry = aSolution.ares[i];
    		if (lastScore!=-1) {
    			if (lastScore>arentry.vector.score) {
    				throw new RuntimeException("lastScore more than current score of "+arentry);
    			}
    		}
    		lastScore = arentry.vector.score;
    		if (isSolutionItemCoversRule(arentry, aRule)) {
    			entries.add(i);
    		}
		}
    	return entries;
    }
	public String getWorksetSolutionFilePath(boolean tmp) {
		int wsId = getWorksetId();
		String wsName = getWorksetContext().getWorksetName();
		String tmpSuffix = "";
		if (tmp) {
			tmpSuffix = ".tmp";
		}
		String name = getContext().getRawDataFilePath() + ".workset" +"."+ wsName+".solution."+wsId +tmpSuffix;
		return name;
	}
	
    public boolean isSolutionItemCoversRule(AREntry solItem, Rule aRule) {
    	
    	for (int h=0;h<Rule.HLIS.length;h++) {
            for (int i=0;i<=solItem.vector.getValue(h);i++) {
                int hlIndex = Rule.HLH[h][i];
                int solValue = solItem.rule.getHLValueDigital(hlIndex);
                int ruleValue = aRule.getHLValueDigital(hlIndex);
                if (solValue!=ruleValue) {
                	return false;
                }
            }
        }
    	return true;
    }
    
    public boolean checkAREntry(AREntry aAREntry, Rule aRule) {
    	int solValue = aAREntry.rule.attrValueDigital;
        int ruleValue = aRule.attrValueDigital;
        boolean res = false;
		if (solValue==ruleValue) {
        	res = true;
        } else {
        	res = false;
        }
		return res;
    	
    }
    
    public boolean checkSolution(SolveBestRulesEntry aSolution, Rule aRule) {
    	
    	SolveBestRulesEntry sortedSolution = new SolveBestRulesEntry(this, aSolution, new AREntryComparator());
    	
        
    	int lastMatchedAREntry = getLastMatchingAREntry(sortedSolution, aRule);
    	
    	if (lastMatchedAREntry<0) {
    		return false;
    	}
    	
    	boolean res = checkAREntry(sortedSolution.ares[lastMatchedAREntry], aRule);
    	return res;
    }
    public void printSolution() {
    	if (solution==null) {
    		Printer.println("First execute sol-load to load the solution into memory");
    	}
    	{
    		Printer.println(solution.toString());
    	}
    }

    public void saveSolution(boolean tmp) {
    	try {
    		
    		/*
    		 * sol-print id
    		 * sol-clear [id]
    		 * sol-check id
    		 */
        	String filename = getWorksetSolutionFilePath(tmp);
    		Printer.println("Saving "+filename);
    		
        	File file = new File(filename);
    		if (file.exists()) {
    			file.delete();
    		}
    		
    		if (!tmp) {
    			String filenameTmp = getWorksetSolutionFilePath(true);
        		
            	File fileTmp= new File(filenameTmp);
        		if (fileTmp.exists()) {
            		Printer.println("Removing temp "+filenameTmp);
            		fileTmp.delete();
        		}
    		}
    		Writer w = new FileWriter(file);
    		BufferedWriter bw = new BufferedWriter(w);
    		
    		List printed = new ArrayList();
    		StringBuffer fileOut = new StringBuffer();
    		fileOut.append("#uncovered="+solution.uncovered+"\n");
            fileOut.append("#size="+solution.ares.length+"\n");
            SortedSet ss = new TreeSet(new AREntryComparator());
            
            for (int i = 0; i < solution.ares.length; i++) {
                ss.add(solution.ares[i]);
            } 
            for (Iterator iter = ss.iterator(); iter.hasNext();) {
				AREntry a = (AREntry) iter.next();
				if (!is_in(a, printed)) {
                    fileOut.append(a.rule.ruleNr+"\t"+a.rule.ruleNrGlobal+"\t"+a.vector.score+"\n");
                } else {
                	throw new RuntimeException("what is_in means here???");
                }
			}
    		bw.write(fileOut.toString());
    		bw.close();
    	} catch (Exception ex) {
    		throw new RuntimeException(ex);
    	}
    	
    }
    DefaultCommandHandler command;
    public void solve(DefaultCommandHandler command) {
    	this.command = command;
    	IWorksetItem wi =getWorksetContext().getWorksetItemAt(worksetId);
		WorksetItemRuleDigitalAcceptor acc = new WorksetItemRuleDigitalAcceptor(getContext(),wi);
		int score  = wi.getClusterDescriptor().getVector().score;
		hl_analyse(score, acc);
    }
    
    private void init_load_rules(int score, IRuleAcceptor ruleAcceptor) {
    	String csv_file  =  ctx.getPureDigitalDataFilePath();
		ctx.setRootScore(score);
    	hl_rules = getContext().getIOContext().readRulesFromPureDigitalFile(csv_file, ctx.getRulesToLoad(),ruleAcceptor);
    	Printer.println(Printer.currDate()+ " Read "+hl_rules.size()+" records.");
      
    	
    	min_unccovered = hl_rules.size();
        atrFreqMap = new HashMap();
        attrValuesDigital = new HashSet();
        for (Iterator iter = hl_rules.iterator(); iter.hasNext();) {
			Rule aRule = (Rule)iter.next();
			attrValuesDigital.add(Integer.valueOf(aRule.attrValueDigital));
			AttrFreq af = (AttrFreq)atrFreqMap.get(aRule.attrValueDigital);
			if (af==null) {
				af = new AttrFreq();
				af.sampleRule = aRule;
				atrFreqMap.put(aRule.attrValueDigital, af);
			}
			af.counter++;
        }
        attrFreqDigital = new int[attrValuesDigital.size()];
        attrFreqDigitalMap = new HashMap();
        int idx = 0;
        for (Iterator iter = attrValuesDigital.iterator(); iter.hasNext();) {
        	Integer attrValueDig = (Integer)iter.next();
        	attrFreqDigitalMap.put(attrValueDig, Integer.valueOf(idx));
        	idx++;
        }
        
        for (Iterator iter = hl_rules.iterator(); iter.hasNext();) {
			Rule aRule = (Rule)iter.next();
			int index = ((Integer)attrFreqDigitalMap.get(Integer.valueOf(aRule.attrValueDigital))).intValue();
			attrFreqDigital[index]++;
        }
        Printer.println(Printer.currDate()+ " Found "+attrFreqDigital.length+" attributes.");
        
        sortedAfSet = new TreeSet(new AttrFreqComparator());
        for (Iterator iter = atrFreqMap.keySet().iterator(); iter.hasNext();) {
			Integer val = (Integer) iter.next();
			sortedAfSet.add(atrFreqMap.get(val));
		}
    	
    }
    private void init_const() {
    	rules_number = hl_rules.size();
        candidatesArray = new long[rules_number];
        
        eval_rules = new int[rules_number];
        
        MainConst.EVAL_BIN_FILLED_LONG_SIZE = rules_number/MainConst.EVAL_BIT_LENGTH;
        MainConst.EVAL_BIN_TOTAL_LONG_SIZE = MainConst.EVAL_BIN_FILLED_LONG_SIZE;
        MainConst.EVAL_BIN_LAST_MASK = 0l;
        MainConst.EVAL_BIN_LAST_BIT_LENGTH = rules_number - MainConst.EVAL_BIN_FILLED_LONG_SIZE*MainConst.EVAL_BIT_LENGTH;
    	if (MainConst.EVAL_BIN_FILLED_LONG_SIZE*MainConst.EVAL_BIT_LENGTH!=rules_number) {
        	for (int i = 0; i < rules_number-MainConst.EVAL_BIN_FILLED_LONG_SIZE*MainConst.EVAL_BIT_LENGTH; i++) {
                MainConst.EVAL_BIN_LAST_MASK = MainConst.EVAL_BIN_LAST_MASK | 1l<<(MainConst.EVAL_BIT_LENGTH-i-1);
			}
            MainConst.EVAL_BIN_TOTAL_LONG_SIZE++;
        }
        MainConst.EVAL_BIN_FULL_MASK = 0l;
    	for (int i = 0; i < MainConst.EVAL_BIT_LENGTH; i++) {
            MainConst.EVAL_BIN_FULL_MASK = MainConst.EVAL_BIN_FULL_MASK | 1l<<(i);
		}
    
    	
    	eval_rules_plus = new long[MainConst.EVAL_BIN_TOTAL_LONG_SIZE];
    	eval_rules_minus = new long[MainConst.EVAL_BIN_TOTAL_LONG_SIZE];
    	
    	candidates2 = new SolveCandidates2(rules_number, this);
    	hl_vectors = Utils.getHLVectorList(Utils.getHLVectorIterator(ctx.getRootScore())) ;
        bestRules = new SolveBestRules(this);


       
    }
    
    
    public void compute_hash_codes_for_rules(int vectors_size) {
    	Printer.println(Printer.currDate()+" Allocating "+hl_rules.size()*hl_vectors.size()*4/1024/1024+" MB for hashcodes...");
    	hl_rules_hashcodes = new int[hl_rules.size()][];
    	hl_rules_hashcode_size = vectors_size;
    	for (int i = 0; i < hl_rules.size(); i++) {
    		hl_rules_hashcodes[i] = new int[hl_vectors.size()];
//			Rule aRule = (Rule)hl_rules.get(i);
//			for (int s = 0; s < hl_rules_hashcode_size; s++) {
//				HLVector vector = (HLVector)hl_vectors.get(s);
//				int[] digs = new int[Rule.HLIS.length];
//				for (int h = 0; h < Rule.HLIS.length; h++) {
//			         int hlIndex = Rule.HLH[h][vector.getValue(h)];
//			         digs[h] = aRule.getHLValueDigital(hlIndex);
//			    }
//				hl_rules_hashcodes[i][s] = HashCodeUtil.hashCode(digs);
//				
//			}
		}
    	Printer.println(Printer.currDate()+" compute_hash_codes_for_rules ok");
    	
    }
    
    
    
    public void hl_analyse(int score, IRuleAcceptor ruleAcceptor) {
    	Printer.println(Printer.currDate()+ " Solve started");
        
    	init_load_rules(score, ruleAcceptor);
        
        if (attrFreqDigital.length==1) {
        	Rule aRule = (Rule)hl_rules.get(0);
        	SolveBestRulesEntry solution = new SolveBestRulesEntry(this);
        	solution.ares = new AREntry[1];
        	AREntry solutionEntry = new AREntry();
        	solutionEntry.rule = aRule;
        	solutionEntry.vector = new HLVector(score);
        	solution.ares[0] = solutionEntry;
            bestRules = new SolveBestRules(this);
            bestRules.add(solution);
        } else {
        	
        
        
	        int max1=-1;
	        int max2=-1;
	        int max3=-1;
	        for (int i = 0; i < attrFreqDigital.length; i++) {
				if ((max3==-1)||(attrFreqDigital[i]>attrFreqDigital[max3])) {
					max3 = i;
				}
			}
	        for (int i = 0; i < attrFreqDigital.length; i++) {
				if (((max2==-1)||(attrFreqDigital[i]>attrFreqDigital[max2]))&&(max3!=i)) {
					max2 = i;
				}
			} 
	        for (int i = 0; i < attrFreqDigital.length; i++) {
				if (((max1==-1)||(attrFreqDigital[i]>attrFreqDigital[max1]))&&(max2!=i)&&(max3!=i)) {
					max1 = i;
				}
			}  
	        
	        Rule rule1=null,rule2=null;
	        
	        
	        for (Iterator iter = hl_rules.iterator(); iter.hasNext();) {
				Rule aRule = (Rule)iter.next();
				int index = ((Integer)attrFreqDigitalMap.get(Integer.valueOf(aRule.attrValueDigital))).intValue();
				
				if (max2==index) {
					rule2 = aRule;
				} else
				if (max3==index) {
					rule1 = aRule;
				}
	        }
	        
	        
	        
	        List arl = new ArrayList();
	        TIMER1 = 0;
	        TIMER2 = 0;
	        TIMER3 = 0;
	      
	        init_const(); 
	        int differentAv = sortedAfSet.size(); 
	        if (differentAv==1) {
	            Printer.println("all of one attribute value!!!");
	        } else {
	        	
	        	
	        	
	            int c =0;
	            for (Iterator iter = sortedAfSet.iterator(); iter.hasNext();) {
					AttrFreq attrFreq = (AttrFreq) iter.next();
					candidates2.setNext(attrFreq.sampleRule.ruleNr, c, true);
					c++;
				}
	            
	            List arl_short = new ArrayList();
	            int forBegin = differentAv;
	            if (differentAv>2) {
	                forBegin = 2;
	            }
	            AR nextAr = null;
	            boolean wasFullScan = false;
	             
	            if (solution!=null) {
	            	Printer.println("Loading tmp solution (one rule shorter)...");
	            	SolveBestRulesEntry bre = new SolveBestRulesEntry(this);
	            	bre.ares = new AREntry[solution.ares.length-1];
	            	for (int i = 0; i < solution.ares.length-1; i++) {
	            		AREntry oldEntry = solution.ares[i];
	            		hl_vectors = new ArrayList();
	            		hl_vectors.add(new HLVector(oldEntry.vector.score));
	            		AR newAr = candidates2.getAR(oldEntry.rule.ruleNr, oldEntry.vector.score);
	            		AREntry newEntry = null;
	            		for (Iterator iter = newAr.entriesList.iterator(); iter.hasNext();) {
	                        AREntry are = (AREntry)iter.next();
	                        if (oldEntry.vector.score==are.vector.score) {
	                        	newEntry = are;
	                        	break;
	                        }
	                    }
	            		bre.ares[i] = newEntry;
	            		if (newEntry==null) {
	            			throw new RuntimeException("AREntry not found: rule="+oldEntry.rule.ruleNr+" score="+oldEntry.vector.score);
	            		}
	            		yield();
	    	            
					}
	            	Printer.println("Evaluating tmp solution...");
	            	eval_rules_bin(bre.ares);
	                print_rec(bre.ares);
	                Printer.println("Next candidate = last rule from tmp solution...");
	                candidates2.setNext(solution.ares[solution.ares.length-1].rule.ruleNr,true);
	                hl_vectors = Utils.getHLVectorList(Utils.getHLVectorIterator(ctx.getRootScore())) ;
	                wasFullScan = true;
	            } else {
		            nextAr = candidates2.getAR(rule1.ruleNr, getContext().getRootScore());
		        	arl_short.add(nextAr);
		        	candidates2.setNext(rule2.ruleNr,true);
	                
		        }
	            
	            yield();
	            for (int i=forBegin-1;i<hl_rules.size();i++) {
	                long t1 = System.currentTimeMillis();
	                
	            	int nextRuleNr = candidates2.getNext();
	            	nextAr = candidates2.getAR(nextRuleNr,getContext().getRootScore());
	            	arl_short.add(nextAr);
	                Printer.print(Printer.currDate( )+" Looking for "+arl_short.size()+" rules solution...");
	                //Printer.print(Printer.currDate( ));
	                
	                if (!wasFullScan) {
	                    do_rec(0, new AREntry[0], arl_short);
	                    wasFullScan = true;
	                } else {
	                    Set bestRulesSet  = new TreeSet(new SolveBestRulesComparator(false));
	                    bestRulesSet.addAll(bestRules.getContent());
	                    
	                    bestRules.init();
	                    
	                    for (Iterator iter = bestRulesSet.iterator(); iter.hasNext();) {
	                        SolveBestRulesEntry bre = (SolveBestRulesEntry) iter.next();
	                        do_rec2(nextAr, bre.ares);
	                        
	                    }
	                    
	                }
	                SolveBestRulesEntry best = bestRules.getBest();
	                
	                //Printer.println(" "+Printer.currDate( )+" "+best.ares.length+" rules "+best.uncovered+" uncovered");
	                
	                
	                Printer.print(" solver:");
	                
	                Printer.printInteger(best.ares.length);

	                Printer.printInteger(best.uncovered);
	                
	                Printer.println();
	                if ((best.ares.length % 50)==0) {
	                	command.setProperty("echo", "set x");
	                }
	                yield();
	                
	                String testRuleNrStr = (String)command.getProperty("ruleNr");
	                if ((testRuleNrStr!=null)&&(!"".equals(testRuleNrStr))) {
		                int testRuleNr = Integer.parseInt(testRuleNrStr);
		                Rule testRule = (Rule)hl_rules.get(testRuleNr);
		                Boolean ok = checkSolution(best, testRule);
		                Printer.println("test for rule "+testRuleNr+" "+ok);
	                	List entries = getAlltMatchingAREntries(best, testRule);
	                	for (Iterator iter = entries.iterator(); iter
								.hasNext();) {
							Integer entryId = (Integer) iter.next();
							boolean testOk = checkAREntry(best.ares[entryId], testRule);
							Printer.println(""+entryId+" "+testOk);
							
						}
	                }
	        		
	                System.gc();
	                
	              	Arrays.fill(candidatesArray,0);
	                for (Iterator iter = bestRules.getContent().iterator(); iter.hasNext();) {
	                    SolveBestRulesEntry bre = (SolveBestRulesEntry) iter.next();
	                    eval_rules_bin(bre.ares);
	                    if (bre.uncovered<best.uncovered) {
	                    	SolveBestRulesComparator comparator = new SolveBestRulesComparator(true);
	                    	int t = comparator.compare(bre, best);
	                    	throw new RuntimeException("best solution has more uncovered that non-best one.");
	                    		
	                    }
	                    eval_bin_next_candidate_bin(bre.uncovered,best.uncovered);
	                }
	                int maxCandidate = 0;
	                for (int j = 0; j < candidatesArray.length; j++) {
						if (candidatesArray[j]>candidatesArray[maxCandidate]) {
							maxCandidate = j;
						}
					}
	                
	                
	                candidates2.setNext(maxCandidate,true);
	                long t2 = System.currentTimeMillis();
	                TIMER2 += t2-t1;
	                //Printer.println(Printer.printTime(TIMER1)+ "/"+Printer.printTime(TIMER3)+ "/"+Printer.printTime(TIMER2));
	                if (best.uncovered==0) {
	                	break;
	                }
	            }
	        }
	
	        // printing top five sets
	        int TOP_SIZE = 1;
	        int cnt = 1;
	        
	        for (Iterator iter = bestRules.getContent().iterator(); iter.hasNext();) {
	        	SolveBestRulesEntry element = (SolveBestRulesEntry) iter.next();
	        	Printer.print("Best rule #"+cnt+":  ");
	        	Printer.print(" " + element.ares.length+ " rules");
	        	if (element.uncovered>0) {
	        		Printer.print(" uncovered= "+element.uncovered);
	        	}
	        	Printer.println("");
	        	//Printer.print(element.toString());
	        	Printer.println("============================");
	        	if (element.uncovered>0) break;
	        	cnt++;
	        	if (cnt>TOP_SIZE) {
	        		break;
	        	}
			}
        }

        SolveBestRulesEntry theBest = (SolveBestRulesEntry)bestRules.getContent().iterator().next();
        solution = theBest;
        printSolution();
        saveSolution(false);
    }
    
    public void sayCurrent() {
    	if (bestRules==null) {
    		Printer.println("bestRules is null");
    	}
    	if (bestRules.getContent().size()==0) {
    		Printer.println("bestRules is empty");
    	}
        SolveBestRulesEntry theBest = (SolveBestRulesEntry)bestRules.getContent().iterator().next();
        solution = theBest;
        printSolution();
        saveSolution(true);

    }
    
    public void yield() {
		command.applyExternalProps();
		if (command.getProperty("echo")!=null) {
			sayCurrent();
			command.setProperty("echo",null);
		}
		
	}
    
    public int covered_points_bin() {
    	int l = eval_rules_plus.length;
    	if (MainConst.EVAL_BIN_LAST_MASK!=0l) {
    		l--;
    	}
    	int p = 0;
        for (int i = 0; i < l; i++) {
        	if (eval_rules_plus[i]==MainConst.EVAL_BIN_FULL_MASK) {
        		p+=MainConst.EVAL_BIT_LENGTH;
            		
        	} else if (eval_rules_plus[i]==0) {
        	} else {
        		p+=count_bits_in_long(eval_rules_plus[i]);
        	}
        } 
        if (MainConst.EVAL_BIN_LAST_MASK!=0l) {
        	p+=count_bits_in_long(eval_rules_plus[l]);
        }
        return p;
    }

    public int count_bits_in_long(long l) {
    	long m = MainConst.EVAL_BIN_TOP_MASK;
    	int c = 0;
    	for (int i = 0; i < MainConst.EVAL_BIT_LENGTH; i++) {
			if ((l&m)!=0) {
				c++;
			}
			m = m>>1;
		}
    	return c;
    }

    public int not_covered_points_bin() {
    	return rules_number - covered_points_bin();
    }

    public void print_rec(List ares) {
        print_rec((AREntry[])ares.toArray(new AREntry[ares.size()]));
    }
    public void print_rec(AREntry[] ares) {
        List printed = new ArrayList();
       
        int nnp =not_covered_points_bin();
        
//        if ((eval_rules_hidden!=null)&&(min_unccovered>=nnp)) {
//            min_unccovered=nnp;
//            if (nnp>0) {
//                for(int i=0;i<eval_rules_hidden.length;i++) {
//                    if (eval_rules_hidden[i]<=0) {
//                        int ruleNr = i;
//                        candidates2.setNext(ruleNr);
//                    }
//                }
//            }
//        }
//        
        if ((ares!=null)&&(ares.length!=0)) {
        	
            SolveBestRulesEntry br = bestRules.getFreeInstance();
            br.ares = ares;
            br.uncovered = nnp;
            bestRules.add(br);	
        }
        

        
        boolean e = eval_bin();
        if (e) {
//            if (min_cov==-1) {
//            	min_cov = ares.length+1;
//            }
            int cc = 0;
            StringBuffer sb = new StringBuffer();
            SortedSet ss = new TreeSet(new AREntryComparator());
            
            for (int i = 0; i < ares.length; i++) {
                ss.add(ares[i]);
            } 
            for (Iterator iter = ss.iterator(); iter.hasNext();) {
				AREntry a = (AREntry) iter.next();
				if (!is_in(a, printed)) {
                    //int s = Utils.printRuleAccordingToVectorDigital(a.rule, a.vector);
                    sb.append(Utils.getScore(a.vector)+" "+ " ->"+a.rule.attrValueDigital+"\n");
                    printed.add(a);
                    cc++;
                }
			}
           // Printer.println(sb.toString()+""+ cc +"-----------------------------------------------------");
        
//            if (min_cov>cc) {
//            	min_cov = cc;
//            }
        }
    }
    
    public boolean is_in(AREntry are, List ares) {
//        int sLast = Utils.printRuleAccordingToVectorDigital(are.rule, are.vector);
        for (int i = 0; i < ares.size(); i++) {
            AREntry a = (AREntry )ares.get(i);
            
            if (Utils.compare(are.rule, a.rule, are.vector, a.vector)==0) {
            	return true;
            }
            
//            int s = Utils.printRuleAccordingToVectorDigital(a.rule, a.vector);
//            if (sLast==s) {
//                return true;
//            }
        } 
        return false;
    }

    public int is_in(AREntry are, AREntry[] ares) {
        //int sLast = Utils.printRuleAccordingToVectorDigital(are.rule, are.vector);
        for (int i = 0; i < ares.length; i++) {
            AREntry a = (AREntry )ares[i];
            //int s = Utils.printRuleAccordingToVectorDigital(a.rule, a.vector);
            //if (sLast==s) {
            if (Utils.equals(are.rule, a.rule, are.vector, a.vector)) {	
                return i;
            }
        } 
        return -1;
    }

    public boolean eval_bin() {
    	int l = eval_rules_plus.length;
    	if (MainConst.EVAL_BIN_LAST_MASK!=0l) {
    		l--;
    	}
        for (int i = 0; i < l; i++) {
        	if (eval_rules_plus[i]!=MainConst.EVAL_BIN_FULL_MASK) return false;
        } 
        if (MainConst.EVAL_BIN_LAST_MASK!=0l) {
        	if (eval_rules_plus[l]!=MainConst.EVAL_BIN_LAST_MASK) return false;
        }	
        return true;
    }
    
    public AR addStillNotUserEntriesDigital(AR ar, AREntry[] ares) {
        AR newAr = new AR();
        newAr.entriesList = new LinkedList();
        newAr.rule = ar.rule;
        for (Iterator iter = ar.entriesList.iterator(); iter.hasNext();) {
            AREntry are = (AREntry)iter.next();
            int sLast = Utils.printRuleAccordingToVectorDigital(are.rule, are.vector)+Integer.valueOf(are.rule.attrValueDigital).hashCode();
            boolean found = false;
            for (int i=0;i<ares.length;i++) {
                AREntry anotherAre= ares[i];
                int sAnother = Utils.printRuleAccordingToVectorDigital(anotherAre.rule, anotherAre.vector)+Integer.valueOf(anotherAre.rule.attrValueDigital).hashCode();
                if (sAnother==sLast) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                newAr.entriesList.add(are);
            }
        }
        return newAr;
    }

    public void do_rec2(AR ar, AREntry[] ares ) {
    	long t1 = System.currentTimeMillis();
    	eval_rules_by_groups_and_unroll_bin(ares);
        print_rec(ares);
        if (!eval_bin()) {
            AR aNewAR = ar;
            AR aNewARStrip = addStillNotUserEntries(aNewAR, ares);
            for (Iterator iterator = aNewARStrip.entriesList.iterator(); iterator.hasNext();) {
                AREntry are = (AREntry)iterator.next();
                AREntry[] news;
                int repl = is_in(are, ares);
                if (repl>=0) {
                    news = copy_and_replace(ares, are, repl);
                } else {
                    news = copyPlusOne(ares, are);
                }
                eval_rules_by_groups_and_unroll_bin(news);
                print_rec(news);
            }
        }
        long t2 = System.currentTimeMillis();
        TIMER3 += t2-t1;
        
    }
    
    public AREntry[] copyPlusOne(AREntry[] arr, AREntry newOne) {
        AREntry[] r = new AREntry[arr.length+1];
        int newI = 0;
        boolean toadd = true;
        for (int i = 0; i < arr.length; i++) {
        	if (toadd&&(ARENTRY_COMPARATOR.compare(arr[i], newOne)>0)) {
        		r[newI] = newOne;
        		newI++;
        		toadd = false;
        	}
            r[newI] =arr[i];
            newI++;
        }
        if (toadd) {
            r[arr.length] = newOne;
        }
        return r;
    }
 
    
    public AREntry[] copy_and_replace(AREntry[] arr, AREntry replace, int repl) {
        AREntry[] r = new AREntry[arr.length];
        for (int i = 0; i < arr.length; i++) {
            if (repl == i) {
                r[i] =replace;
            } else {
                r[i] =arr[i];
            }
        }
        return r;
    }

    public AR TO_DELETE_addStillNotUserEntriesx(AR ar, AREntry[] ares) {
        AR newAr = new AR();
        newAr.entriesList = new LinkedList();
        newAr.rule = ar.rule;
        for (Iterator iter = ar.entriesList.iterator(); iter.hasNext();) {
            AREntry are = (AREntry)iter.next();
            int sLast = Utils.printRuleAccordingToVectorDigital(are.rule, are.vector);
            int sLastA = are.rule.attrValueDigital;
            boolean found = false;
            for (int i=0;i<ares.length;i++) {
                AREntry anotherAre= ares[i];
                int sAnother = Utils.printRuleAccordingToVectorDigital(anotherAre.rule, anotherAre.vector);
                int sAnotherA = anotherAre.rule.attrValueDigital;
                if ((sLastA==sAnotherA)&&(sLast==sAnother)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                newAr.entriesList.add(are);
            }
        }
        return newAr;
    }

    public AR addStillNotUserEntries(AR ar, AREntry[] ares) {
        AR newAr = new AR();
        newAr.entriesList = new LinkedList();
        newAr.rule = ar.rule;
        for (Iterator iter = ar.entriesList.iterator(); iter.hasNext();) {
            AREntry are = (AREntry)iter.next();
//            int sLast = Utils.printRuleAccordingToVectorDigital(are.rule, are.vector);
            int sLastA = are.rule.attrValueDigital;
            boolean found = false;
            for (int i=0;i<ares.length;i++) {
                AREntry anotherAre= ares[i];
               // int sAnother = Utils.printRuleAccordingToVectorDigital(anotherAre.rule, anotherAre.vector);
                int sAnotherA = anotherAre.rule.attrValueDigital;
                if ((sLastA==sAnotherA)&&(Utils.compare(are.rule, anotherAre.rule, are.vector,anotherAre.vector)==0)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                newAr.entriesList.add(are);
            }
        }
        return newAr;
    }



    public void eval_bin_next_candidate_bin(int uncovered, int max_uncovered ) {
    	int ruleNr=0;
    	
    	for (int i = 0; i < MainConst.EVAL_BIN_FILLED_LONG_SIZE; i++) {
        	if (eval_rules_plus[i]!=MainConst.EVAL_BIN_FULL_MASK) {
        		long m = MainConst.EVAL_BIN_TOP_MASK;
            	for (int j = 0; j < MainConst.EVAL_BIT_LENGTH; j++) {
        			if ((eval_rules_plus[i]&m)==0) {
        				candidatesArray[ruleNr] = candidatesArray[ruleNr] + (rules_number/(uncovered-max_uncovered+1));
                        if (candidatesArray[ruleNr]<0) {
                            throw new RuntimeException("candidatesArray overloaded");
                        }
        				//candidates2.setNext(i*EVAL_BIT_LENGTH+j);
        			}
        			m = m>>1;
        			ruleNr++;
        		}
        	} else {
        		ruleNr = ruleNr+MainConst.EVAL_BIT_LENGTH;
        	}
        } 
        if (MainConst.EVAL_BIN_LAST_MASK!=0l) {
        	long m = MainConst.EVAL_BIN_TOP_MASK;
        	for (int j = 0; j < MainConst.EVAL_BIN_LAST_BIT_LENGTH; j++) {
    			if ((eval_rules_plus[MainConst.EVAL_BIN_FILLED_LONG_SIZE]&m)==0) {
    				//candidates2.setNext(EVAL_BIN_FILLED_LONG_SIZE*EVAL_BIT_LENGTH+j);
    				candidatesArray[ruleNr] = candidatesArray[ruleNr] + (rules_number/(uncovered-max_uncovered+1));
                    if (candidatesArray[ruleNr]<0) {
                        throw new RuntimeException("candidatesArray overloaded");
                    }
    				}
    			m = m>>1;
      			ruleNr++;
      	   	}
        }  
    }
 
    private void ASSERT_ARES_IS_WELL_SORTED(AREntry[] ares, SortedSet ss) {
    	if (true) return;
    	if (ss.size()!=ares.length) throw new RuntimeException();
    	int i = 0;
    	for (Iterator iter = ss.iterator(); iter.hasNext();) {
			AREntry aREntry = (AREntry) iter.next();
			AREntry aREntry2 = ares[i];
			
			if (ARENTRY_COMPARATOR.compare(aREntry, aREntry2)!=0) {
				throw new RuntimeException();
			}
			i++;
		}
    	
    }
    
    long[] dup_rules_plus = null;
	long[] dup_rules_minus = null;
	
    public void eval_rules_temp(AREntry[] ares) {
    	eval_rules_bin(ares);
    	eval_rules_bin(ares);
    	eval_rules_bin(ares);
    	eval_rules_bin(ares);
    	eval_rules_bin(ares);
    	eval_rules_bin(ares);
    	eval_rules_bin(ares);
    	eval_rules_bin(ares);
    	
    	if (dup_rules_plus==null) {
    		dup_rules_plus = new long[eval_rules_plus.length];
    		dup_rules_minus = new long[eval_rules_plus.length];
    		tmp_rules_plus = new long[eval_rules_plus.length];
    		tmp_rules_minus = new long[eval_rules_plus.length];
    	}
    	for (int i = 0; i < eval_rules_plus.length; i++) {
    		dup_rules_plus[i]= eval_rules_plus[i];
    		dup_rules_minus[i]= eval_rules_minus[i];
		}
    	eval_rules_by_groups_bin(ares);
    	for (int i = 0; i < eval_rules_plus.length; i++) {
    		if (dup_rules_plus[i]!= eval_rules_plus[i]) throw new RuntimeException();
    		if (dup_rules_minus[i]!= eval_rules_minus[i]) throw new RuntimeException();
		}
     }
    
    public void eval_rules_bin(AREntry[] ares) {
        if (ares.length==0) {
        	eval_rules_plus[0]=0;
        	eval_rules_minus[0]=255;
        	return;
        }
        //ASSERT_ARES_IS_WELL_SORTED(ares, ss);
        Arrays.fill(eval_rules_plus, 0l);
        Arrays.fill(eval_rules_plus, 0l);
        for (int j = 0; j < ares.length; j++) {
            AREntry a = ares[j];
            for (int i = 0; i < a.binPlus.length; i++) {
            	long old_plus = eval_rules_plus[i]&(~a.binMinus[i]);
            	long old_minus = eval_rules_minus[i]&(~a.binPlus[i]);
            	eval_rules_plus[i] = old_plus|a.binPlus[i];
            	eval_rules_minus[i] = old_minus|a.binMinus[i];
         	}
        }
    }
    long[] tmp_rules_plus = null;
	long[] tmp_rules_minus = null;
	
    private void eval_rules_by_groups_bin(AREntry[] ares) {

    	if (tmp_rules_plus==null) {
    		tmp_rules_plus = new long[eval_rules_plus.length];
    		tmp_rules_minus = new long[eval_rules_plus.length];
    	}
    	if (ares.length==0) {
        	eval_rules_plus[0]=0;
        	eval_rules_minus[0]=255;
        	return;
        }
        
    	Arrays.fill(eval_rules_plus, 0l);
        Arrays.fill(eval_rules_plus, 0l);
        
    	int from = -1;
    	int fromScore = -1;
    	
    	
    	for (int j = 0; j < ares.length+1; j++) {
    		if (j==ares.length) {
            	merge_ares_of_same_score(ares, from, j-1, tmp_rules_plus, tmp_rules_minus);
            	apply_merged_plusminus(tmp_rules_plus, tmp_rules_minus);
    		} else {
                AREntry a = ares[j];
                if (from==-1) {
                	from = 0;
                	fromScore = a.vector.score;
                }
                if (fromScore!=a.vector.score) {
                	merge_ares_of_same_score(ares, from, j-1, tmp_rules_plus, tmp_rules_minus);
                	apply_merged_plusminus(tmp_rules_plus, tmp_rules_minus);
                	from = j;
                }
    		}
        }
    }
    
    private void eval_rules_by_groups_and_unroll_bin(AREntry[] ares) {

    	if (tmp_rules_plus==null) {
    		tmp_rules_plus = new long[eval_rules_plus.length];
    		tmp_rules_minus = new long[eval_rules_plus.length];
    	}
    	if (ares.length==0) {
        	eval_rules_plus[0]=0;
        	eval_rules_minus[0]=255;
        	return;
        }
        
    	Arrays.fill(eval_rules_plus, 0l);
        Arrays.fill(eval_rules_plus, 0l);
        
    	int from = -1;
    	int fromScore = -1;
    	
    	
    	for (int j = 0; j < ares.length+1; j++) {
    		if (j==ares.length) {
            	merge_ares_of_same_score_UNROLL10(ares, from, j-1, tmp_rules_plus, tmp_rules_minus);
            	apply_merged_plusminus(tmp_rules_plus, tmp_rules_minus);
    		} else {
                AREntry a = ares[j];
                if (from==-1) {
                	from = 0;
                	fromScore = a.vector.score;
                }
                if (fromScore!=a.vector.score) {
                	merge_ares_of_same_score_UNROLL10(ares, from, j-1, tmp_rules_plus, tmp_rules_minus);
                	apply_merged_plusminus(tmp_rules_plus, tmp_rules_minus);
                	from = j;
                }
    		}
        }
    }

    
    private void tst_eval_bin(AREntry[] ares) {
    	long[] eval_rules_plus1 = new long[eval_rules_plus.length];
        long[] eval_rules_minus1= new long[eval_rules_plus.length];
    	eval_rules_bin(ares);
		tst_copy_eval_bin(eval_rules_plus1,eval_rules_minus1);
		eval_rules_by_groups_and_unroll_bin(ares);
		
		tst_ASSERT_compare_eval_bins(
    			eval_rules_plus1, eval_rules_minus1,
    			eval_rules_plus, eval_rules_minus
    			);
    	
    }
    
    
    
    
    
    
    private void apply_merged_plusminus(long[] result_plus, long[] result_minus) {
    	for (int i = 0; i < result_plus.length; i++) {
        	long old_plus = eval_rules_plus[i]&(~result_minus[i]);
        	long old_minus = eval_rules_minus[i]&(~result_plus[i]);
        	eval_rules_plus[i] = old_plus|result_plus[i];
        	eval_rules_minus[i] = old_minus|result_minus[i];
     	}
    }
    
    
    private void merge_ares_of_same_score(AREntry[] ares, int from, int to, long[] result_plus, long[] result_minus) {
    	Arrays.fill(result_plus, 0l);
        Arrays.fill(result_minus, 0l);
    	
    	for (int j = from; j <= to; j++) {
            AREntry a = ares[j];
            for (int i = 0; i < a.binPlus.length; i++) {
            	result_plus[i] = result_plus[i]|a.binPlus[i];
            	result_minus[i] = result_minus[i]|a.binMinus[i];
         	}
        }
    }

    private void merge_ares_of_same_score_UNROLL10(AREntry[] ares, int from, int to, long[] result_plus, long[] result_minus) {
    	Arrays.fill(result_plus, 0l);
        Arrays.fill(result_minus, 0l);
    	
        int j = from;
        while (j<=to) {
        	if (((to-j)>=10)) {
	            for (int i = 0; i < ares[j].binPlus.length; i++) {
	            	result_plus[i] = result_plus[i]|
	            	ares[j].binPlus[i]|
	            	ares[j+1].binPlus[i]|
	            	ares[j+2].binPlus[i]|
	            	ares[j+3].binPlus[i]|
	            	ares[j+4].binPlus[i]|
	            	ares[j+5].binPlus[i]|
	            	ares[j+6].binPlus[i]|
	            	ares[j+7].binPlus[i]|
	            	ares[j+8].binPlus[i]|
	            	ares[j+9].binPlus[i]
	            	
	             ;
	            	
	            	result_minus[i] = result_minus[i]|
	            	ares[j].binMinus[i]|
	            	ares[j+1].binMinus[i]|
	            	ares[j+2].binMinus[i]|
	            	ares[j+3].binMinus[i]|
	            	ares[j+4].binMinus[i]|
	            	ares[j+5].binMinus[i]|
	            	ares[j+6].binMinus[i]|
	            	ares[j+7].binMinus[i]|
	            	ares[j+8].binMinus[i]|
	            	ares[j+9].binMinus[i]
	             ;
	         	}
        		j  = j + 10;
        	} else {
	            AREntry a = ares[j];
	            for (int i = 0; i < a.binPlus.length; i++) {
	            	result_plus[i] = result_plus[i]|a.binPlus[i];
	            	result_minus[i] = result_minus[i]|a.binMinus[i];
	         	}
	            j++;
        	}
        }
    }

    
    private void TO_DELETE_eval_rules_compute_c() {
        int p =0;
        int b = 0;
        long mask = 1l<<(MainConst.EVAL_BIT_LENGTH-1);
        for (int i=0;i<rules_number;i++) {
        	boolean pl = (eval_rules_plus[p]&mask)!=0;
        	boolean mi = (eval_rules_minus[p]&mask)!=0;
        	if (pl) {
        		eval_rules[i] = 1;
        	} else
        	if (mi) {
        		eval_rules[i] = -1;
        	} else {
        		eval_rules[i] = 0;
        	}
        	b++;
        	mask = mask >>1;
        	if (b==MainConst.EVAL_BIT_LENGTH) {
        		b=0;
        		p++;
        		mask = 1l<<MainConst.EVAL_BIT_LENGTH-1;
        	}
        }
    }

    public int do_rec(int rule_num, AREntry[] ares, List arl ) {
    	eval_rules_bin(ares);
        if (rule_num==arl.size()) {
            print_rec(ares);
            return 0;
        } else {
            int c= 0;
            if (eval_bin()) {
                print_rec(ares);
                return 0;
            } else {
                AR aNewAR = (AR)arl.get(rule_num);
                AR aNewARStrip = addStillNotUserEntries(aNewAR, ares);
                for (Iterator iterator = aNewARStrip.entriesList.iterator(); iterator.hasNext();) {
                    AREntry are = (AREntry)iterator.next();
                    AREntry[] news;
                    int repl = is_in(are, ares);
                    if (repl>=0) {
                        news = copy_and_replace(ares, are, repl);
                    } else {
                        news = copyPlusOne(ares, are);
                    }
                    c = c + do_rec(rule_num+1, news, arl);
                }
            }
            return c;
        }
    }
    
    public AR computeAR(Rule aRule, List hl_rules, List vectors, int base_score, SolveContext solveContext) {
        ARComputeContext mainCtx = new ARComputeContext();
        ARComputeContext tmpCtx = new ARComputeContext();
        
    	AR ar = new AR();
		ar.entriesMap =  new HashMap();
		ar.rule = aRule;
		Printer.print("RuleNr:");
		Printer.printInteger(aRule.ruleNr);
		Printer.printInteger(aRule.attrValueDigital);

        int c = 0;
        int aHash = 0;
        int anotherHash = 0;
        HLVector aVector = null;
        
        for (int v = 0; v < vectors.size(); v++) {
	    	aVector = (HLVector)vectors.get(v);
//                aHash = hl_rules_hashcodes[aRule.ruleNr][v];
	            tmpCtx._plus.clear();
	            tmpCtx._minus.clear();

	            for (int i = 0; i < hl_rules.size(); i++) {
					Rule anotherRule = (Rule)hl_rules.get(i);
//					anotherHash = hl_rules_hashcodes[i][v];
//					boolean b = Utils.equals(aRule, anotherRule, aVector);
					//boolean b2  = aHash==anotherHash;
					//if  (false){
					if (Utils.equals(aRule, anotherRule, aVector)) {
//					if (aHash==anotherHash) {
//		    			if (aHash!=anotherHash) {
//		    				throw new RuntimeException("hash problems anotherRule "+i+" v="+v);
//		    			}
//                        if ((aVector.score==7880704l)&&(aRule.ruleNr!=anotherRule.ruleNr)) {
//                            throw new RuntimeException("Same hash code for rules "+aRule.ruleNr+" and "+anotherRule.ruleNr+"!");
//                        }
                        
	    				if (anotherRule.attrValueDigital== aRule.attrValueDigital) {
	    					tmpCtx._plus.add(anotherRule);
	    				} else {
	    					tmpCtx._minus.add(anotherRule);
	    				}
//	    			} else {
//		    			if (aHash==anotherHash) {
//		    				int t = 1;
//		    				Utils.equals(aRule, anotherRule, aVector);
//		    				throw new RuntimeException("hash problems anotherRule "+i+" v="+v);
//		    			}
//
	    			}
	    			
	        	}
	        
	        	AREntry are = new AREntry();
                mainCtx.clear();
                mainCtx._minus.addAll(tmpCtx._minus);
                mainCtx._plus.addAll(tmpCtx._plus);
	        	String sb = aREntryPlusMinusString(are, mainCtx);
	        	are.vector = aVector;
                are.rule = aRule;
     
	        	ar.entriesMap.put(sb, are);
	        	are.computeBinaryPlusMinus(solveContext.rules_number, mainCtx);
                c++;
	    }
        int originalPossibleRules = ar.entriesMap.keySet().size();
        if (ctx.isVerbose()) {
        	for (Iterator iter = ar.entriesMap.keySet().iterator(); iter.hasNext();) {
				String key = (String) iter.next();
				AREntry are = (AREntry)ar.entriesMap.get(key);
				Printer.println(ScoreHelper.printHLVectorAsArray(are.vector)+"  "+ are.toString());
				
			}
        	Printer.println("----");
        }
        //ar.removeHeadAndTail();
        if (ctx.isVerbose()) {
        	Printer.println("after removing head and tail----");
        	for (Iterator iter = ar.entriesMap.keySet().iterator(); iter.hasNext();) {
				String key = (String) iter.next();
				AREntry are = (AREntry)ar.entriesMap.get(key);
				Printer.println(ScoreHelper.printHLVectorAsArray(are.vector)+"  "+ are.toString());
				
			}
        	Printer.println("----");
        }
        Printer.printInteger(ar.entriesMap.keySet().size());
        Printer.print("   ");
		
       // Printer.println();
//        Printer.print(""+ar.entriesMap.keySet().size()+" ("+originalPossibleRules+")"+" possible rules\n");
        
        return ar;
    	
    }
    

    
    public AR computeARold(Rule aRule, List hl_rules, List vectors, int base_score, SolveContext solveContext) {
        ARComputeContext mainCtx = new ARComputeContext();
        ARComputeContext tmpCtx = new ARComputeContext();
        
    	AR ar = new AR();
		ar.entriesMap =  new HashMap();
		//ar.negativesMap = new HashMap<String, AREntry> ();
		ar.rule = aRule;
        Printer.print(Printer.currDate( )+" Computing +- for record "+aRule.ruleNr+" `"+aRule.attrValueDigital+"` from score "+base_score+"."); 
        int c = 0;
        HLVector aVector = null;
        for (Iterator hlVectorIterator = vectors.iterator(); hlVectorIterator.hasNext();) {
		    	aVector = (HLVector)hlVectorIterator.next();
                
	            //Integer aHash = new Integer(Utils.hashCodeForProjection(aRule, aVector));
	            tmpCtx._plus.clear();
	            tmpCtx._minus.clear();

                for (Iterator iter2 = hl_rules.iterator(); iter2.hasNext();) {
	    			Rule anotherRule = (Rule)iter2.next();
	    			//if (aRule==anotherRule) continue;
	    			//Integer anotherHash = new Integer(Utils.hashCodeForProjection(anotherRule, aVector));
	    			if (Utils.equals(aRule, anotherRule, aVector)) {//aHash.equals(anotherHash)) {
//                        if ((aRule.ruleNr!=anotherRule.ruleNr)) {//(aVector.score==7880704l)&&
//                            throw new RuntimeException("Same hash code for rules "+aRule.ruleNr+" and "+anotherRule.ruleNr+"!");
//                        }
                        
	    				if (anotherRule.attrValueDigital== aRule.attrValueDigital) {
	    					tmpCtx._plus.add(anotherRule);
	    				} else {
	    					tmpCtx._minus.add(anotherRule);
	    				}
	    			}
	        	}
	        
	        	AREntry are = new AREntry();
                mainCtx.clear();
                mainCtx._minus.addAll(tmpCtx._minus);
                mainCtx._plus.addAll(tmpCtx._plus);
	        	String sb = aREntryPlusMinusString(are, mainCtx);
	        	are.vector = aVector;
                are.rule = aRule;
     
                
	        	ar.entriesMap.put(sb, are);
	        	are.computeBinaryPlusMinus(solveContext.rules_number, mainCtx);
                if ((c%100)==0) {
                    Printer.print(".");
                }
                c++;
	    }
        int originalPossibleRules = ar.entriesMap.keySet().size();
        if (ctx.isVerbose()) {
        	for (Iterator iter = ar.entriesMap.keySet().iterator(); iter.hasNext();) {
				String key = (String) iter.next();
				AREntry are = (AREntry)ar.entriesMap.get(key);
				Printer.println(ScoreHelper.printHLVectorAsArray(are.vector)+"  "+ are.toString());
				
			}
        	Printer.println("----");
        }
        //ar.removeHeadAndTail();
        if (ctx.isVerbose()) {
        	Printer.println("after removing head and tail----");
        	for (Iterator iter = ar.entriesMap.keySet().iterator(); iter.hasNext();) {
				String key = (String) iter.next();
				AREntry are = (AREntry)ar.entriesMap.get(key);
				Printer.println(ScoreHelper.printHLVectorAsArray(are.vector)+"  "+ are.toString());
				
			}
        	Printer.println("----");
        }
        Printer.print(""+ar.entriesMap.keySet().size()+" ("+originalPossibleRules+")"+" possible rules\n");
        
        return ar;
    	
    }
    
    String aREntryPlusMinusString(AREntry are, ARComputeContext ctx) {
        StringBuffer sb = new StringBuffer("+");
        for (int i = 0; i < ctx._plus.size(); i++) {
            sb.append(""+((Rule)ctx._plus.get(i)).ruleNr+" ");
        };
        sb.append("/ -");
        for (int i = 0; i < ctx._minus.size(); i++) {
            sb.append(""+((Rule)ctx._minus.get(i)).ruleNr+" ");
        };
        return sb.toString();
    }
	public void close() {
	}

}
